/*
 * @Author: Kyle
 * @Date: 2022-01-10
 * @Description: Provide a tool to transform json and data class (require：not inherits MonoBehaivour and with a not-parameter constructor). 
 */

using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using LitJson;

public class JsonTool
{
    /// <summary>
    /// 转换对象数组Json (适用于通过 excel2json 转出来的 json文件)
    /// </summary>
    public static List<T> ToObjectList<T>(string strJson)
    {
        List<T> list = new List<T>();
        JsonData datas = JsonMapper.ToObject(strJson);
        foreach (JsonData item in datas)
        {
            T instance = (T)Activator.CreateInstance(typeof(T));
            object o = _ConvertClass(item, typeof(T), instance);
            if (instance != null)
                list.Add((T)o);
        }
        return list;
    }

    /// <summary>
    /// 转换单个对象Json
    /// </summary>
    public static T ToObject<T>(string strJson)
    {
        JsonData data = JsonMapper.ToObject(strJson);
        T instance = (T)Activator.CreateInstance(typeof(T));
        return (T)_ConvertClass(data, typeof(T), instance);
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////
    
    #region ToObject

    private static object _ConvertClass(JsonData json, Type classType, object instance)
    {
        if (json == null) return instance;

        FieldInfo[] fields = classType.GetFields();
        foreach (var info in fields)
        {
            info.SetValue(instance, _Convert(info.FieldType, json[info.Name]));
        }
        return instance;
    }

    private static object _Convert(Type t, JsonData jd)
    {
        if (jd.IsArray)
        {
            if (t.IsArray)
            {
                return _Str2Array(jd, t.GetElementType());
            }
            else if (t.IsGenericType)
            {
                Type[] ts = t.GetGenericArguments();

                if (ts.Length == 1)
                {
                    return _Str2List(jd, ts[0]);
                }
                else if (ts.Length == 2)
                {
                    return _Str2Dic(jd, ts[0], ts[1]);
                }
            }
        }
        else //variable && define class
        {
            string str = jd.ToString();
            if (typeof(bool) == t) return Convert.ToBoolean(str);
            else if (typeof(char) == t) return Convert.ToChar(str);
            else if (typeof(string) == t) return Convert.ToString(str);
            else if (typeof(ushort) == t) return Convert.ToUInt16(str);
            else if (typeof(short) == t) return Convert.ToInt16(str);
            else if (typeof(uint) == t) return Convert.ToUInt32(str);
            else if (typeof(int) == t) return Convert.ToInt32(str);
            else if (typeof(long) == t) return Convert.ToInt64(str);
            else if (typeof(float) == t) return Convert.ToSingle(str);
            else if (typeof(double) == t) return Convert.ToDouble(str);
            else if (typeof(Vector3) == t) return _Str2Vector3(str);//三维向量
            else if (typeof(Vector2) == t) return _Str2Vector2(str);//二维向量
            else if (typeof(Quaternion) == t) return _Str2Quaternion(str);
            else if (typeof(Color) == t) return _Str2Color(str);//颜色
            else if (t.IsEnum) return Enum.Parse(t, str);//枚举值
            else
            {
                object o = Activator.CreateInstance(t);
                return _ConvertClass(jd, t, o);
            }
        }
        return null;
    }

    #region strJson to unityValue (Vector3 / Vector2 / Quaternion / Color / Enum)
    // excel 中的类型填 string

    private static Vector3 _Str2Vector3(string str)
    {
        Vector3 v = new Vector3();
        str = str.Substring(1, str.Length - 2);
        string[] strs = str.Split(',');
        if (strs.Length == 3)
        {
            v.x = float.Parse(strs[0]);
            v.y = float.Parse(strs[1]);
            v.z = float.Parse(strs[2]);
        }
        return v;
    }

    private static Vector2 _Str2Vector2(string str)
    {
        Vector2 v = new Vector2();
        str = str.Substring(1, str.Length - 2);
        string[] strs = str.Split(',');
        if (strs.Length == 2)
        {
            v.x = float.Parse(strs[0]);
            v.y = float.Parse(strs[1]);
        }
        return v;
    }

    private static Quaternion _Str2Quaternion(string str)
    {
        Quaternion q = new Quaternion();
        str = str.Substring(1, str.Length - 2);
        string[] strs = str.Split(',');
        if (strs.Length == 4)
        {
            q.x = float.Parse(strs[0]);
            q.y = float.Parse(strs[1]);
            q.z = float.Parse(strs[2]);
            q.w = float.Parse(strs[3]);
        }
        return q;
    }

    private static Color _Str2Color(string str)
    {
        Color color = Color.white;
        string[] strs = str.Split(',');
        if (strs.Length == 4)
        {
            color = new Color(float.Parse(strs[0]), float.Parse(strs[2]), float.Parse(strs[2]), float.Parse(strs[3]));
        }
        return color;
    }

    private static object _Str2List(JsonData jd, Type elementType)
    {
        var genericType = typeof(List<>).MakeGenericType(elementType);     //构建出 itemType 类型的 List
        var genericList = Activator.CreateInstance(genericType);
        var addMethod = genericType.GetMethod("Add");

        if (jd != null)
        {
            foreach (JsonData item in jd)
            {
                addMethod.Invoke(genericList, new object[] { _Convert(elementType, item) });
            }
        }
        return genericList;
    }

    private static object _Str2Array(JsonData jd, Type elementType)
    {
        int count = jd.Count;
        int index = 0;
        var array = Array.CreateInstance(elementType, count);
        foreach (JsonData item in jd)
        {
            array.SetValue(_Convert(elementType, item), index++);
        }
        return array;
    }

    private static object _Str2Dic(JsonData jd, Type elementType, Type elementType2)
    {
        Type[] typeArgs = { elementType, elementType2 };
        var genericType = typeof(Dictionary<,>).MakeGenericType(typeArgs);     //构建出 itemType 类型的 List
        var genericList = Activator.CreateInstance(genericType);
        var addMethod = genericType.GetMethod("Add");

        if (jd != null)
        {
            foreach (JsonData item in jd)
            {
                addMethod.Invoke(genericList, new object[] { _Convert(elementType, item[0]), _Convert(elementType2, item[1]) });
            }
        }
        return genericList;
    }
    #endregion

    #endregion
}
